package n7.restaurant.java.Entity;

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.StringTokenizer;

import java.util.Iterator;

import n7.restaurant.java.Enum.Acces;
import n7.restaurant.java.Enum.Etat_Client;
import n7.restaurant.java.Enum.Etat_Serveur;
import n7.restaurant.java.Enum.Role;
import n7.restaurant.java.Swing.Swing;

public class Serveur extends Agent {
	public static ArrayList<Serveur> serveurs = new ArrayList<Serveur>();
	private ArrayList<Client> clients = new ArrayList<Client>();
	private String toString;
	private int numeroTache;

	public Serveur() {
		super();
	}

	public Serveur(String id, Role role, ArrayList<Etat_Mental> etat_mentaux,
			Position position) {
		super(id, role, etat_mentaux, position);
	}

	public int getNumeroTache() {
		return numeroTache;
	}

	public void setNumeroTache(int numeroTache) {
		this.numeroTache = numeroTache;
	}

	public Serveur(int id, ArrayList<Client> clients) {
		super();
		this.clients = clients;
	}

	public String getToString() {
		return toString;
	}

	public void setToString(String toString) {
		this.toString = toString;
	}

	public Serveur(int id) {
		super();
		this.clients = new ArrayList<Client>();
	}

	public ArrayList<Client> getClients() {
		return clients;
	}

	public void setClients(ArrayList<Client> clients) {
		this.clients = clients;
	}

	@Override
	// le methode pour imprimer les etat mentaux
	public String toString() {
		String returnString = this.getId() + "\n";
		// Imprimer les etat mentaux du serveurs.
		ArrayList<Etat_Mental> etats = this.getEtat_mentaux();
		if (!etats.isEmpty()) {
			for (int i = 0; i < etats.size(); i++) {
				Etat_Mental e = etats.get(i);
				returnString += e.toString() + "\n";
			}
		}
		returnString += "knowref(" + this.getId() + ",etat(" + this.getId()
				+ ")=" + this.getEtat().toString() + ")";
		return returnString;
	}

	/**
	 * le methode du thread
	 * */
	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			// Quand les etat mentaux change, traverser au l'autre 'if'.

			// est-ce qu'il y a la commande de menu?
			if (this.getEtat() == Etat_Serveur.free && hasClientDemandeMenu()) {
				// informer des autres serveurs
				/*
				 * System.out.println("_________"+this+" "+this.getEtat_mentaux()
				 * .size());
				 */
				synchronized (Serveur.class) {
					Client c = getDemandeMenuClient();
					if (c == null) {
						// il n'y a pas de client qui demande menu.
						continue;
					} else {
						// il y a des client qui demande menu
						informerAutreServeur("knowref",
								"wantDemandeMenu(" + c.getId() + ")=true",
								"livre_menu(" + c.getId() + ")");
						// know(C1, livrer_menu(SV1))
						Etat_Mental e = new Etat_Mental();
						e.setDescription("know");
						e.setFormule("venir_livrer_menu(" + this.getId() + ")");
						e.setY(c);
						int numClient = Integer
								.parseInt(c.getId().substring(1));
						/* System.out.println(numClient); */
						Client.clients.get(numClient).addEtatMental(e);
						// Le serveur est indisponible.
						this.setEtat(Etat_Serveur.doing);
						/* System.out.println(c); */

						// Ajouter
						// l'etat_mental au serveur
						// want(SV1, livrer_menu(C0))
						e = new Etat_Mental();
						e.setDescription("want");
						e.setFormule("livrer_menu(" + c.getId() + ")");
						e.setAgent_X(this);
						this.addEtatMental(e);
						this.clients.add(c);
						/* System.out.println(this); */
					}
				}
			} else if (this.getEtat() == Etat_Serveur.doing
					&& hasLivrerMenuTask()) {
				// Le serveur a un task de livrer menu et pas arriver.
				int numbreClient = getLivrerMenuClient();
				Client c = Client.clients.get(numbreClient);
				/* System.out.println(c.getId()); */
				moveToTask(c.getPosition());
				// ajouter
				// know(C4, arrive(SV2))
				Etat_Mental e = new Etat_Mental();
				e.setY(c);
				e.setDescription("know");
				e.setFormule("arrive(" + this.getId() + ")");
				Client.clients.get(numbreClient).addEtatMental(e);
			} else if (recevoirCommand()) {
				// if il a un tache a donner au chef
				Position tachePosition = Chef.grand_chef.getPosition();
				Acces lieuA = this.getPosition().getLieu();
				Acces lieuB = tachePosition.getLieu();
				// si deux lieu different, traverser la salle
				if (lieuA != lieuB) {
					Position portePosition = Porte.getAccesPosition(lieuA,
							lieuB);
					passerSalle(portePosition);
					moveToTask(tachePosition);
				}
				// le serveur arrive au chef, donner le menu.
				informChefCommande();
				/* System.out.println(this); */
				// return to compteur
				int i = Integer.parseInt(this.getId().substring(2));
				Position compteur = new Position(
						(Salle_Principale.LONGUEUR_SALLE_PRINCIPALE - 7),
						5 + i * 5, Acces.SALLE_PRINCIPALE);

				lieuA = this.getPosition().getLieu();
				lieuB = compteur.getLieu();
				// si deux lieu different, traverser la salle
				if (lieuA != lieuB) {
					Position portePosition = Porte.getAccesPosition(lieuA,
							lieuB);
					passerSalle(portePosition);
					moveToTask(compteur);
				}
				this.setEtat(Etat_Serveur.free);
			} else if (hasPlatPret()) {
				// System.out.println((this.getEtat() == Etat_Serveur.doing)
				// + " waiting");
				int numeroClient = -1;
				// cherche
				// knowref(SV1, possede(C1,type)=pret)
				for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
					Etat_Mental em = this.getEtat_mentaux().get(i);
					// trouver
					if (em.getFormule().contains("possede(C")
							&& em.getFormule().contains("pret")) {
						numeroClient = Integer.parseInt(em.getFormule()
								.substring(9, 10));
						String type = em.getFormule().substring(11);
						StringTokenizer st = new StringTokenizer(type, ")",
								true);
						type = st.nextToken();
						// entree est pret, le client attend l'entree.
						if (type.equalsIgnoreCase("entree")
								&& Client.clients.get(numeroClient).getEtat() == Etat_Client.waitEntree) {
							donnerPlatSimplifier(type, numeroClient);
							this.getEtat_mentaux().remove(i);
							break;
						} else if (type.equalsIgnoreCase("plat_principal")
						// plat principal est pret, le client attend le plat principal.
								&& Client.clients.get(numeroClient).getEtat() == Etat_Client.waitPlat) {
							donnerPlatSimplifier(type, numeroClient);
							this.getEtat_mentaux().remove(i);
							break;
						} else if (type.equalsIgnoreCase("dessert")
						// dessert est pret, le client attend le dessert.
								&& Client.clients.get(numeroClient).getEtat() == Etat_Client.waitDessert) {
							donnerPlatSimplifier(type, numeroClient);
							this.getEtat_mentaux().remove(i);
							break;
						}
					}
				}
			}

			// le but est realisr, le system termine
			if (!hasPlatPret() && butRealiser()) {
				Swing.signalTermine = -1;
			}
		}

	}

	/**
	 * Si le but est realiser ou pas
	 * */
	private boolean butRealiser() {
		// TODO Auto-generated method stub
		for (int i = 0; i < Client.clients.size(); i++) {
			Client c = Client.clients.get(i);
			if (c.getEtat() != Etat_Client.waitPay) {
				return false;
			}
		}
		synchronized (Serveur.class) {
			for (int i = 0; i < 6; i++)
				Swing.TAP[i] += ", informer_que(" + this.getId() + ","
						+ Client.clients.get(i).getId()
						+ ",le_plats_du_menu_sont_complets)";
		}

		return true;
	}

	/**
	 * donner le plat attendu au client
	 * */
	private void donnerPlatSimplifier(String type, int numeroClient) {
		// TODO Auto-generated method stub
		// ajouter
		// entree_pret(C0) -> l'univer
		// changer
		// etat -> next(etat)
		Client c = Client.clients.get(numeroClient);
		if (type.equalsIgnoreCase("entree")) {
			Swing.UNIVER += "entree_pret(C" + numeroClient + "), ";
			c.setEtat(Etat_Client.waitPlat);
			c.addEtatMental(new Etat_Mental("know", c, "pret(" + type + ")"));
		} else if (type.equalsIgnoreCase("plat_principal")) {
			Swing.UNIVER += "plat_principal_pret(C" + numeroClient + "), ";
			c.setEtat(Etat_Client.waitDessert);
			c.addEtatMental(new Etat_Mental("know", c, "pret(" + type + ")"));
		} else if (type.equalsIgnoreCase("dessert")) {
			Swing.UNIVER += "dessert_pret(C" + numeroClient + "), ";
			c.setEtat(Etat_Client.waitPay);
			c.addEtatMental(new Etat_Mental("know", c, "pret(" + type + ")"));
		}
		Swing.TAP[numeroClient] += ", donner_que(" + this.getId() + ","
				+ Client.clients.get(numeroClient).getId() + "," + type + ")";
	}

	/**
	 * Si il y a le plat du client est pret
	 * */

	private boolean hasPlatPret() {
		// TODO Auto-generated method stub

		// cherche
		// knowref(SV1, possede(C1,type)=pret)
		for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
			Etat_Mental em = this.getEtat_mentaux().get(i);
			if (em.getFormule().contains("possede(C")
					&& em.getFormule().contains("pret")) {
				// numeroClient =
				// Integer.parseInt(em.getFormule().substring(9,10 ));
				return true;
			}
		}
		return false;
	}

	/**
	 * Evoluer par ses etat mentaux si il y a des clients qui demande le menu.
	 * 
	 * @return boolean
	 * */
	public boolean hasClientDemandeMenu() {

		for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
			try {
				Etat_Mental e = this.getEtat_mentaux().get(i);
				// knowref(SV0, demandeMenu(C0)=true)
				if (e.getDescription().equalsIgnoreCase("knowref")
						&& e.getFormule().contains("wantDemandeMenu")) {
					return true;
				}
			} catch (ConcurrentModificationException e2) {

			}
		}
		return false;
	}

	/**
	 * Obtenir le client qui veut demande un menu.
	 * 
	 * @return Client
	 * */
	public Client getDemandeMenuClient() {
		for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
			Etat_Mental e = this.getEtat_mentaux().get(i);
			// knowref(SV0, wantDemandeMenu(C0)=true)
			if (e.getDescription().equalsIgnoreCase("knowref")
					&& e.getFormule().contains("wantDemandeMenu")) {
				int n = Integer.parseInt(e.getFormule().substring(17, 18));
				return Client.clients.get(n);
			}
		}
		return null;
	}

	/**
	 * informer aux autre serveurs
	 * */
	public void informerAutreServeur(String description, String formule,
			String contenu) {
		synchronized (Serveur.class) {
			// supprimer les etat mental dans serveur.
			int numeroClient = Integer.parseInt(formule.substring(17, 18));
			this.setNumeroTache(numeroClient);

			for (int i = 0; i < Serveur.serveurs.size(); i++) {
				int numeroSupprimer = -1;
				Serveur s = Serveur.serveurs.get(i);
				for (int j = 0; j < s.getEtat_mentaux().size(); j++) {
					try {
						Etat_Mental e = s.getEtat_mentaux().get(j);
						/*
						 * System.out.println(description + " " + formule + " "
						 * + contenu);
						 */
						if (e.getDescription().equalsIgnoreCase(description)
								&& e.getFormule().equalsIgnoreCase(formule)) {
							numeroSupprimer = j;
						}
					} catch (ConcurrentModificationException e1) {
						break;
					}

				}
				if (numeroSupprimer != -1) {
					s.getEtat_mentaux().remove(numeroSupprimer);
				}
			}

			Swing.TAP[numeroClient] += ", informer_que(" + this.getId() + ","
					+ "_" + ",traiter(" + this.getId() + ",C" + numeroClient
					+ "))";
			System.out.println("Action: informer(" + this.getId()
					+ ",Serveurs," + contenu + ")");
		}
	}

	/**
	 * deplacer au position du task
	 * */
	public void moveToTask(Position p) {
		int id = Integer.parseInt(this.getId().substring(2, 3));

		int this_x = this.getPosition().getX();
		int this_y = this.getPosition().getY();
		Serveur s = Serveur.serveurs.get(id);
		if (this_y >= p.getY()) {
			while (Math.abs(this_y - p.getY()) >= 5) {
				this_y -= 3;
				s.setPosition_Y(this_y);
				delay(400);
			}
		} else {
			while (Math.abs(p.getY() - this_y) >= 5) {
				this_y += 3;
				s.setPosition_Y(this_y);
				delay(400);
			}
		}
		if (this_x >= p.getX()) {
			while (Math.abs(this_x - p.getX()) >= 5) {
				this_x -= 3;
				s.setPosition_X(this_x);
				delay(400);
			}
		} else {
			while (Math.abs(p.getX() - this_x) >= 5) {
				this_x += 3;
				s.setPosition_X(this_x);
				delay(400);
			}
		}
		this.setEtat(Etat_Serveur.waiting);
		// Action:
		// Swing.TAP[this.getNumeroTache()] += ", aller(" + this.getId()
		// + ",destination" + "))";

	}

	/**
	 * Si le serveur a le task a livrer menu ou pas
	 * */
	public boolean hasLivrerMenuTask() {
		if (this.getEtat_mentaux() != null) {
			for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
				Etat_Mental e = this.getEtat_mentaux().get(i);
				/* System.out.println(this.getId() + " Not null"); */
				if (e.getDescription().equalsIgnoreCase("want")
						&& e.getFormule().contains("livrer_menu"))
					return true;
			}
		}
		return false;
	}

	/**
	 * Si le serveur a le task a livrer menu ou pas
	 * */
	public int getLivrerMenuClient() {
		if (this.getEtat_mentaux() != null) {
			/* System.out.println("Not null"); */
			for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
				Etat_Mental e = this.getEtat_mentaux().get(i);
				if (e.getDescription().equalsIgnoreCase("want")
						&& e.getFormule().contains("livrer_menu")) {
					/*
					 * System.out.println(e.getFormule());
					 * System.out.println(Integer.parseInt(e.getFormule()
					 * .substring(13, 14)));
					 */
					return Integer.parseInt(e.getFormule().substring(13, 14));
				}
			}
		}
		return -1;
	}

	public void setPosition_X(int x) {
		this.getPosition().setX(x);
	}

	public void setPosition_Y(int y) {
		this.getPosition().setY(y);
	}

	public void delay(int time) {
		try {
			Thread.sleep(time);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Est-ce que le serveur a recu la commande du client?
	 * */
	public boolean recevoirCommand() {
		// want(SVi, knowref(GC0,commande(C4)=M0)));
		for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
			Etat_Mental e = this.getEtat_mentaux().get(i);
			if (e.getDescription().equalsIgnoreCase("want")
					&& e.getFormule().contains("knowref(GC0,commande(")) {
				/* System.out.println("Etat:" + this.getEtat()); */
				this.setEtat(Etat_Serveur.doing);
				return true;
			}
		}
		return false;
	}

	/**
	 * traverser la salle
	 * */
	public void passerSalle(Position p) {
		int id = Integer.parseInt(this.getId().substring(2, 3));

		int this_x = this.getPosition().getX();
		int this_y = this.getPosition().getY();
		Serveur s = Serveur.serveurs.get(id);
		if (this_y >= p.getY()) {
			while (Math.abs(this_y - p.getY()) >= 3) {
				this_y -= 3;
				s.setPosition_Y(this_y);
				delay(400);
			}
			s.setPosition_Y(p.getY());
		} else {
			while (Math.abs(p.getY() - this_y) >= 3) {
				this_y += 3;
				s.setPosition_Y(this_y);
				delay(400);
			}
			s.setPosition_Y(p.getY());
		}
		if (this_x >= p.getX()) {
			while (Math.abs(this_x - p.getX()) >= 3) {
				this_x -= 3;
				s.setPosition_X(this_x);
				delay(400);
			}
			s.setPosition_X(p.getX());
		} else {
			while (Math.abs(p.getX() - this_x) >= 3) {
				this_x += 3;
				s.setPosition_X(this_x);
				delay(400);
			}
			s.setPosition_X(p.getX());
		}
		s.getPosition().setLieu(p.getLieu());
	}

	/**
	 * tell chef that which menu has been ordered.
	 * */
	public void informChefCommande() {
		Chef c = Chef.grand_chef;
		int numeroClient = -1;
		int numeroMenu = -1;
		synchronized (Serveur.class) {
			for (int i = 0; i < this.getEtat_mentaux().size(); i++) {
				Etat_Mental em = this.getEtat_mentaux().get(i);
				// want(SVi, knowref(GC0,commande(C4)=M0));
				if (em.getDescription().equalsIgnoreCase("want")
						&& em.getFormule().contains(
								"knowref(" + c.getId() + ",commande(")) {
					// knowref(GC0,commande(C4)=M0);
					c.addEtatMental(new Etat_Mental("knowref", c, em
							.getFormule().substring(12, 27)));
					numeroClient = Integer.parseInt(em.getFormule().substring(
							22, 23));
					numeroMenu = Integer.parseInt(em.getFormule().substring(26,
							27));
					this.getEtat_mentaux().remove(i);
				}
			}
		}
		// Action:
		Swing.TAP[numeroClient] += ", informer(" + this.getId() + ","
				+ c.getId() + ",C_" + numeroClient + "commande_M" + numeroMenu
				+ "))";
	}
}
